package net.ruixin.orm.hibernate;

import net.ruixin.utils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SimpleHibernateTemplate<T, PK extends Serializable>
        implements ISimpleHibernateTemplate<T, PK> {
    private Log logger = LogFactory.getLog(super.getClass());
    public SessionFactory sessionFactory;
    protected Class<T> entityClass;

    public SimpleHibernateTemplate() {
        this.entityClass = null;
        Class c = super.getClass();
        Type t = c.getGenericSuperclass();
        if (t instanceof ParameterizedType) {
            Type[] p = ((ParameterizedType) t).getActualTypeArguments();
            this.entityClass = ((Class) p[0]);
        }
    }

    public SimpleHibernateTemplate(SessionFactory sessionFactory, Class<T> entityClass) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {
        return this.sessionFactory.getCurrentSession();
    }

    public SessionFactory getSessionFactory() {
        return this.sessionFactory;
    }

    @Autowired
    public void setSessionFactory(@Qualifier("sessionFactory") SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void save(T entity) {
        Assert.notNull(entity);
        getSession().saveOrUpdate(entity);
        this.logger.info("save entity: {}" + entity);
    }

    public void delete(T entity) {
        Assert.notNull(entity);
        getSession().delete(entity);
        this.logger.info("delete entity: {}" + entity);
    }

    public void delete(PK id) {
        Assert.notNull(id);
        delete(get(id));
    }

    public List<T> findAll() {
        return findAll((OrderBy[]) null);
    }

    public List<T> findAll(OrderBy[] orders) {
        Criteria crit = createCriteria(new Criterion[0]);

        if (orders != null) {
            for (OrderBy order : orders) {
                crit.addOrder(order.getOrder());
            }
        }
        return crit.list();
    }

    public Page<T> findAll(Page<T> page) {
        return findByCriteria(page, new Criterion[0]);
    }

    public T get(PK id) {
        return (T) getSession().get(this.entityClass, id);
    }

    public List find(String hql, Object... values) {
        return createQuery(hql, values).list();
    }

    public Page<T> find(Page<T> page, String hql, Object... values) {
        Assert.notNull(page);

        if (page.isAutoCount()) {
            page.setTotalCount(countQueryResult(hql, values));
//            this.logger.warn("HQL查询暂不支持自动获取总结果数,hql为{}" + hql);
        }
        Query q = createQuery(hql, values);

        if (page.isFirstSetted()) {
            q.setFirstResult(page.getFirst());
        }
        if (page.isPageSizeSetted()) {
            q.setMaxResults(page.getPageSize());
        }
        page.setResult(q.list());
        return page;
    }

    public Object findUnique(String hql, Object... values) {
        return createQuery(hql, values).uniqueResult();
    }

    public Integer findInt(String hql, Object... values) {
        return (Integer) findUnique(hql, values);
    }

    public Long findLong(String hql, Object... values) {
        return (Long) findUnique(hql, values);
    }

    public List<T> findByCriteria(Criterion[] criterion) {
        return createCriteria(criterion).list();
    }

    public Page<T> findByCriteria(Page page, Criteria c) {
        Assert.notNull(page);

        //Criteria c = createCriteria(criterion);

        if (page.isAutoCount()) {
            page.setTotalCount(countQueryResult(page, c));
        }
        if (page.isFirstSetted()) {
            c.setFirstResult(page.getFirst());
        }
        if (page.isPageSizeSetted()) {
            c.setMaxResults(page.getPageSize());
        }

        if (page.isOrderBySetted()) {
            if (page.getOrder().endsWith("asc"))
                c.addOrder(Order.asc(page.getOrderBy()));
            else {
                c.addOrder(Order.desc(page.getOrderBy()));
            }
        }
        page.setResult(c.list());
        return page;
    }

    public Page<T> findByCriteria(Page page, Criterion[] criterion) {
        Assert.notNull(page);

        Criteria c = createCriteria(criterion);

        if (page.isAutoCount()) {
            page.setTotalCount(countQueryResult(page, c));
        }
        if (page.isFirstSetted()) {
            c.setFirstResult(page.getFirst());
        }
        if (page.isPageSizeSetted()) {
            c.setMaxResults(page.getPageSize());
        }

        if (page.isOrderBySetted()) {
            if (page.getOrder().endsWith("asc"))
                c.addOrder(Order.asc(page.getOrderBy()));
            else {
                c.addOrder(Order.desc(page.getOrderBy()));
            }
        }
        page.setResult(c.list());
        return page;
    }

    public Page<T> findByCriteria(Page page, List<Criterion> criterion) {
        Assert.notNull(page);

        Criteria c = createCriteria(criterion);

        if (page.isAutoCount()) {
            page.setTotalCount(countQueryResult(page, c));
        }
        if (page.isFirstSetted()) {
            c.setFirstResult(page.getFirst());
        }
        if (page.isPageSizeSetted()) {
            c.setMaxResults(page.getPageSize());
        }

        if (page.isOrderBySetted()) {
            if (page.getOrder().endsWith("asc"))
                c.addOrder(Order.asc(page.getOrderBy()));
            else {
                c.addOrder(Order.desc(page.getOrderBy()));
            }
        }
        page.setResult(c.list());
        return page;
    }

    public List<T> findByProperty(String propertyName, Object value, OrderBy[] orders) {
        Assert.hasText(propertyName);
        Criteria crit = createCriteria(new Criterion[]{Restrictions.eq(propertyName, value)});

        if (orders != null) {
            for (OrderBy order : orders) {
                crit.addOrder(order.getOrder());
            }
        }

        return crit.list();
    }

    public T findUniqueByProperty(String propertyName, Object value) {
        Assert.hasText(propertyName);
        return (T) createCriteria(new Criterion[]{Restrictions.eq(propertyName, value)}).uniqueResult();
    }

    /**
     * 去除select 子句，未考虑union的情况
     */
    private static String removeSelect(String hql) {
        Assert.hasText(hql);
        int beginPos = hql.toLowerCase().indexOf("from");
        Assert.isTrue(beginPos != -1, " hql : " + hql + " must has a keyword from");
        return hql.substring(beginPos);
    }

    /**
     * 去除orderby 子句
     */
    private static String removeOrders(String hql) {
        Assert.hasText(hql);
        Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(hql);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, "");
        }
        m.appendTail(sb);
        return sb.toString();
    }

    public Query createQuery(String queryString, Object... values) {
        Assert.hasText(queryString);
        Query queryObject = getSession().createQuery(queryString);
        if (values != null) {
            for (int i = 0; i < values.length; ++i) {
                queryObject.setParameter(i, values[i]);
            }
        }
        return queryObject;
    }

    public Criteria createCriteria(Criterion[] criterions) {
        Criteria criteria = getSession().createCriteria(this.entityClass);
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }

    public Criteria createCriteria(List<Criterion> criterions) {
        Criteria criteria = getSession().createCriteria(this.entityClass);
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }

    public boolean isPropertyUnique(String propertyName, Object newValue, Object orgValue) {
        if ((newValue == null) || (newValue.equals(orgValue))) {
            return true;
        }
        Object object = findUniqueByProperty(propertyName, newValue);
        return object == null;
    }

    protected int countQueryResult(String hql, Object... values) {
        // 执行Count查询
        int totalCount = 0;
        String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
        try {
            totalCount = ((Long) createQuery(countQueryString, values).uniqueResult()).intValue();
        } catch (Exception e) {
            totalCount = (Integer) createQuery(countQueryString, values).uniqueResult();
        }
        return totalCount;
    }

    protected int countQueryResult(Page<T> page, Criteria c) {
        CriteriaImpl impl = (CriteriaImpl) c;

        Projection projection = impl.getProjection();
        ResultTransformer transformer = impl.getResultTransformer();

        List orderEntries = null;
        try {
            orderEntries = (List) BeanUtils.getFieldValue(impl, "orderEntries");
            BeanUtils.setFieldValue(impl, "orderEntries", new ArrayList());
        } catch (Exception e) {
            this.logger.error("不可能抛出的异常:{}", e);
        }

        int totalCount = ((Long) c.setProjection(Projections.rowCount()).uniqueResult()).intValue();

        c.setProjection(projection);

        if (totalCount == 0) {
            return 0;
        }

        if (projection == null) {
            c.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
        }
        if (transformer != null) {
            c.setResultTransformer(transformer);
        }
        try {
            BeanUtils.setFieldValue(impl, "orderEntries", orderEntries);
        } catch (Exception e) {
            this.logger.error("不可能抛出的异常:{}", e);
        }

        return totalCount;
    }

    public SQLQuery createSQLQuery(String queryString, Object... values) {
        Assert.hasText(queryString);
        SQLQuery queryObject = getSession().createSQLQuery(queryString);
        if (values != null) {
            for (int i = 0; i < values.length; ++i) {
                queryObject.setParameter(i, values[i]);
            }
        }
        return queryObject;
    }

    public List findBySQL(String sql, Object... values) {
        return createSQLQuery(sql, values).list();
    }

    public void executeSQL(String sql, Object... values) {
        createSQLQuery(sql, values).executeUpdate();
    }

    public boolean exists() {
        return createCriteria(new Criterion[0]).setMaxResults(1).list().size() > 0;
    }

    public boolean existsQuery(String hql, Object... values) {
        return createQuery(hql, values).setMaxResults(1).list().size() > 0;
    }

    public boolean existsSQLQuery(String sql, Object... values) {
        return createSQLQuery(sql, values).setMaxResults(1).list().size() > 0;
    }
}

/* Location:           E:\ruixin-core.jar
 * Qualified Name:     net.ruixin.orm.hibernate.SimpleHibernateTemplate
 * JD-Core Version:    0.5.4
 */